import datetime
from selenium.webdriver.edge.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from abc import abstractmethod,ABC

class MouseOperationBase(ABC):
    def __init__(self):    
        pass

    def click(self) -> None:
        self.element.click()

    def move_to_element(self) -> None:
        ActionChains(driver=self.driver).move_to_element(self.element).perform()

    def move_to_element_with_offset(self, x: int, y: int) -> None:
        ActionChains(driver=self.driver).move_to_element_with_offset(
            to_element=self.element, xoffset=x, yoffset=y).perform()

    def move_by_offset(self, x: int, y: int) -> None:
        ActionChains(driver=self.driver).move_by_offset(xoffset=x, yoffset=y).perform()

    def scroll_click_element(self) -> None:
        ActionChains(driver=self.driver).scroll_to_element(self.element).perform()

    def script_hover(self, duration: float = 0.5, offset_x: int = 0, offset_y: int = 0) -> None:
        js_script = """
                    function simulateHover(element, duration, offsetX, offsetY) {
                        const rect = element.getBoundingClientRect();
                        const centerX = rect.left + rect.width / 2 + offsetX;
                        const centerY = rect.top + rect.height / 2 + offsetY;
                        
                        const mouseMoveEvent = new MouseEvent('mousemove', {
                            view: window,
                            bubbles: true,
                            cancelable: true,
                            clientX: centerX,
                            clientY: centerY
                        });
                        
                        const mouseOverEvent = new MouseEvent('mouseover', {
                            view: window,
                            bubbles: true,
                            cancelable: true,
                            clientX: centerX,
                            clientY: centerY
                        });
                        
                        const mouseEnterEvent = new MouseEvent('mouseenter', {
                            view: window,
                            bubbles: true,
                            cancelable: true,
                            clientX: centerX,
                            clientY: centerY
                        });
                        
                        document.dispatchEvent(mouseMoveEvent);
                        element.dispatchEvent(mouseOverEvent);
                        element.dispatchEvent(mouseEnterEvent);
                        
                        if (duration > 0) {
                            return new Promise(resolve => {
                                setTimeout(resolve, duration * 1000);
                            });
                        }
                    }
                    
                    const promise = simulateHover(arguments[0], arguments[1], arguments[2], arguments[3]);
                    
                    if (promise) {
                        const done = arguments[arguments.length - 1];
                        promise.then(done);
                    }
                    """

        self.driver.execute_async_script(js_script, self.element, duration, offset_x, offset_y)

    def script_scroll(self) -> None:
        self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});", self.element)

    def script_click(self) -> None:
        self.driver.execute_script("arguments[0].click();", self.element)

    def safe_click(self) -> None:
        try:
            self.driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});", self.element)
            self.element.click()
        except:
            self.driver.execute_script("arguments[0].click();", self.element)

    def condition_click(self,condition_xpath:str,timeout=5,expect_exists=True) -> None:
        self.safe_click()
        _et = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
        while datetime.datetime.now() < _et:
            try:
                self.driver.find_element(by=By.XPATH,value=condition_xpath)
                if expect_exists:
                    return True
            except:
                if not expect_exists:
                    return True
                continue
        return False
    
    @property
    @abstractmethod
    def element() -> WebElement:
        pass
    
    @property
    @abstractmethod
    def driver() -> WebDriver:
        pass
    
